package jwt

import (
	"context"
	"errors"
	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v5"
	"go_blok/server/global"
	"time"
)

// CustomClaims 自定义声明（可添加用户ID、角色等业务字段）
type CustomClaims struct {
	UserID               uint   `json:"user_id"`
	Username             string `json:"username"`
	RoleList             []uint `json:"role"`
	jwt.RegisteredClaims        // 嵌入标准声明（包含过期时间、签发时间等）
}

// GenerateToken 生成JWT
func GenerateToken(cus CustomClaims) (string, error) {
	j := global.Config.Jwt
	// 设置过期时间
	expirationTime := time.Now().Add(time.Duration(j.ExpiresAt) * time.Hour)

	// 创建声明
	claims := CustomClaims{
		UserID:   cus.UserID,
		Username: cus.Username,
		RoleList: cus.RoleList,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(expirationTime), // 过期时间
			IssuedAt:  jwt.NewNumericDate(time.Now()),     // 签发时间
			NotBefore: jwt.NewNumericDate(time.Now()),     // 生效时间（立即生效）
			Issuer:    j.Issuer,                           // 签发者（可选）
		},
	}

	// 创建token（使用 HMAC SHA256 签名算法）
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

	// 签名并获取完整token字符串
	tokenString, err := token.SignedString([]byte(j.SecretKey))
	return tokenString, err

}

// ParseToken 解析Token
func ParseToken(tokenString string) (*CustomClaims, error) {
	j := global.Config.Jwt

	// 解析token（传入自定义声明类型和签名密钥）
	token, err := jwt.ParseWithClaims(
		tokenString,
		&CustomClaims{}, // 需要传入指针接收解析后的声明数据

		// 回调函数用于验证签名密钥 (可根据token.Header.Alg选择不同密钥)
		func(token *jwt.Token) (interface{}, error) {
			// 验证签名算法是否为预期算法 (防止算法混淆攻击)
			if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
				return nil, jwt.ErrSignatureInvalid
			}
			return []byte(j.SecretKey), nil
		},
	)

	if err != nil {
		return nil, err // token过期/签名错误/格式错误等都会返回err
	}

	// 获取解析后的声明数据
	if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
		return claims, nil // 验证通过，返回自定义声明
	}

	return nil, jwt.ErrSignatureInvalid // token无效
}

// CheckTokenBlack 在redis中检查token是否在黑名单
func CheckTokenBlack(token string) bool {
	_, err := global.Redis.Get(context.Background(), "token_black:"+token).Result()
	if err != nil {
		return false
	}
	return true
}

// GetClaimsFromContext 从上下文中获取claims
func GetClaimsFromContext(c *gin.Context) (*CustomClaims, error) {
	claims, exists := c.Get("claims")
	if !exists {
		return nil, errors.New("请先登录")
	}
	return claims.(*CustomClaims), nil
}
